package fr.ants.model;

import java.util.LinkedList;
import java.util.List;

import fr.ants.model.ai.behaviour.QueenBehavior;
import fr.ants.model.ai.behaviour.WorkerBehavior;
import fr.ants.model.ai.heuristic.IHeuristic;
import fr.ants.model.ai.heuristic.Manhattan;
import fr.ants.model.ai.pathfinding.AStar;
import fr.ants.model.ai.pathfinding.IPathFinder;

/** 
 * <p>Nest of ants. Each nest can define its own path finder and heuristic method.</p>
 *
 * @author Vianney DUPOY DE GUITARD
 */
public class Nest {

	/** The path finder used by all ants from this nest. */
	private IPathFinder pathFinder = new AStar();

	/** Number of resource units initially possessed by the nest. */
	private float startResourceCount;

	/** Number of ants initially possessed by the nest. */
	private float startAntsCount;

	/** Number of resource units possessed by the nest. */
	private float resourceCount;

	/** Initial lifespan of any ant from this nest. */
	private int initialAntLifespan = 200;

	/** Maximum quantity of pheromone. */
	private int pheromoneMaxQty;

	/** Maximum size (in ants) of this nest. */
	private int maxNestSize = 20;

	/** Amount of queens that pops out after each swarm. */
	private int queenQtyPerSwarm = 1;

	/** Minimum delay between two swarms, in cycles. */
	private int delayBetweenSwarms = 300;

	/** Qty of cycles until a next swarm is possible (set to {@link #delayBetweenSwarms} when a swarm occurs). */
	private int cyclesUntilNextSwarm = 0;

	/** The heuristic method used by all ants from this nest. */
	private IHeuristic heuristic = new Manhattan();

	/** List of the ants in this nest. */
	private LinkedList<Ant> ants = new LinkedList<>();

	/** The node where is the current nest hole. */
	private Node node;

	/**
	 * Creates a new nest.
	 * @param pNode : the node where is located the nest.
	 * @param antsCount : initial amount of ants in the nest.
	 * @param pResourceCount : initial quantity of food.
	 */
	public Nest(Node pNode, int antsCount, float pResourceCount) {
		node = pNode;
		startResourceCount = pResourceCount;
		resourceCount = pResourceCount;
		pathFinder.setHeuristic(heuristic);
		startAntsCount = antsCount;
		for (int i = 0; i < antsCount; i++) {
			ants.add(new Ant(node, this, new WorkerBehavior()));
		}
	}

	/** 
	 * @return all the ants from this nest.
	 */
	public List<Ant> getAnts() {
		return ants;
	}

	/** Consume the resources of the nest, depending of its ant count. */
	public void consumeResources() {
		resourceCount -= ants.size() / 100;
	}

	/**
	 * Add food to the nest.
	 * @param res : amount of food added.
	 */
	public void addResource(float res) {
		resourceCount += res;
		while (resourceCount / ants.size() > 10 && ants.size() < maxNestSize) {
			ants.add(new Ant(getNode(), this, new WorkerBehavior()));
		}
		if (ants.size() == maxNestSize && cyclesUntilNextSwarm == 0) {
			swarm();
		}
	}

	/**
	 * The nest swarms queens to spread.
	 */
	private void swarm() {
		for (int i = 0; i < queenQtyPerSwarm; i++) {
			ants.add(new Ant(getNode(), this, new QueenBehavior(initialAntLifespan)));
		}
		cyclesUntilNextSwarm = delayBetweenSwarms;
	}

	/** @return the node where the nest is. */
	public Node getNode() {
		return node;
	}

	/** @return the path finder used by the ants from this nest. */
	public IPathFinder getPathFinder() {
		return pathFinder;
	}

	/**
	 * Sets the path finder used by the ants from this nest.
	 * @param pathFinder : the new path finder.
	 */
	public void setPathFinder(IPathFinder pathFinder) {
		this.pathFinder = pathFinder;
	}

	/** @return the heuristic used by the ants from this nest. */
	public IHeuristic getHeuristic() {
		return heuristic;
	}

	/**
	 * Sets the heuristic used by the ants from this nest.
	 * @param heuristic : the new heuristic.
	 */
	public void setHeuristic(IHeuristic heuristic) {
		this.heuristic = heuristic;
	}

	/** Resets the ant. */
	public void reset() {
		resourceCount = startResourceCount;
		ants.clear();
		for (int i = 0; i < startAntsCount; i++) {
			ants.add(new Ant(node, this, new WorkerBehavior()));
		}
	}

	/** @return the amount of resources in the nest. */
	public float getResourceCount() {
		return resourceCount;
	}

	/**
	 * @return the initialAntLifespan
	 */
	public int getInitialAntLifespan() {
		return initialAntLifespan;
	}

	/**
	 * @param initialAntLifespan the initialAntLifespan to set
	 */
	public void setInitialAntLifespan(int initialAntLifespan) {
		this.initialAntLifespan = initialAntLifespan;
	}

	/**
	 * @return the pheromoneMaxQty
	 */
	public int getPheromoneMaxQty() {
		return pheromoneMaxQty;
	}

	/**
	 * @param pheromoneMaxQty the pheromoneMaxQty to set
	 */
	public void setPheromoneMaxQty(int pheromoneMaxQty) {
		this.pheromoneMaxQty = pheromoneMaxQty;
	}

	/**
	 * @return the maxNestSize
	 */
	public int getMaxNestSize() {
		return maxNestSize;
	}

	/**
	 * @param maxNestSize the maxNestSize to set
	 */
	public void setMaxNestSize(int maxNestSize) {
		this.maxNestSize = maxNestSize;
	}

	/**
	 * @return the queenQtyPerSwarm
	 */
	public int getQueenQtyPerSwarm() {
		return queenQtyPerSwarm;
	}

	/**
	 * @param queenQtyPerSwarm the queenQtyPerSwarm to set
	 */
	public void setQueenQtyPerSwarm(int queenQtyPerSwarm) {
		this.queenQtyPerSwarm = queenQtyPerSwarm;
	}

	/**
	 * @return the delayBetweenSwarms
	 */
	public int getDelayBetweenSwarms() {
		return delayBetweenSwarms;
	}

	/**
	 * @param delayBetweenSwarms the delayBetweenSwarms to set
	 */
	public void setDelayBetweenSwarms(int delayBetweenSwarms) {
		this.delayBetweenSwarms = delayBetweenSwarms;
	}

	/** Triggers all the logic related to nests. */
	public void next() {
		consumeResources();
		if (cyclesUntilNextSwarm > 0) {
			cyclesUntilNextSwarm--;
		}
	}

	/**
	 * Copy the settings of a nest.
	 * @param nest : the nest to copy.
	 */
	public void copy(final Nest nest) {
		this.delayBetweenSwarms = nest.delayBetweenSwarms;
		this.initialAntLifespan = nest.initialAntLifespan;
		this.maxNestSize = nest.maxNestSize;
		this.pheromoneMaxQty = nest.pheromoneMaxQty;
		this.startAntsCount = nest.startAntsCount;
		this.heuristic = nest.heuristic;
		this.pathFinder = nest.pathFinder;
	}
}